import 'dart:ui' show Offset;

/// [MultiClickTracker]：一个轻量级的连续点击（单击/双击/三击等）检测工具。
///
/// 功能：
/// - 统一封装多击逻辑（时间窗口、位置容差、次数上限）；
/// - 避免在 `State` 中散落维护多个 `_lastTapTime`、`_lastTapPos` 等变量；
/// - 可在 `GestureDetector.onTapDown` 或其他点击入口处调用 [registerTap]，获得点击次数。
///
/// 使用示例：
/// ```dart
/// final tracker = MultiClickTracker();
///
/// void _handleTapDown(TapDownDetails d) {
///   final count = tracker.registerTap(d.localPosition);
///   if (count == 1) {
///     // 单击
///   } else if (count == 2) {
///     // 双击
///   } else {
///     // 三击及以上（会被夹到 maxClicks）
///   }
/// }
/// ```
///
/// 判定规则：
/// - 在 [timeout] 时间窗口内，且位置与上一次点击距离不超过 [slop]，则认为是连续点击；
/// - 超出时间或距离则重置为 1；
/// - 计数会被限制在 [maxClicks] 范围内（默认 3），避免无限增长。
class MultiClickTracker {
  /// 创建一个多击检测器。
  ///
  /// - [timeout] 连击的最大时间间隔（默认 300ms）；
  /// - [slop] 判定为“同一位置”的最大像素距离（默认 6.0）；
  /// - [maxClicks] 连击次数上限（默认 3，常用于单/双/三击）。
  MultiClickTracker({
    this.timeout = const Duration(milliseconds: 300),
    this.slop = 6.0,
    this.maxClicks = 3,
  }) : assert(maxClicks >= 1, 'maxClicks must be >= 1');

  /// 连击的最大时间窗口。
  final Duration timeout;

  /// 判定为“同一位置”的最大容差（像素单位）。
  final double slop;

  /// 连击次数上限（通常为 3）。
  final int maxClicks;

  DateTime? _lastTapTime;
  Offset? _lastTapPos;
  int _tapCount = 0;

  /// 注册一次点击，并返回当前点击的连击次数（范围 1..[maxClicks]）。
  ///
  /// - [pos] 当前点击位置（应传入 **本地坐标**，如 `TapDownDetails.localPosition`）；
  /// - [now] 可选参数；不传则使用 `DateTime.now()`，主要用于测试场景；
  ///
  /// 返回值：
  /// - 若在 [timeout] 内、且距离小于 [slop]，则在上次计数基础上 +1；
  /// - 否则重置为 1；
  /// - 计数始终会被 `clamp` 到 `[1, maxClicks]` 范围内。
  int registerTap(Offset pos, {DateTime? now}) {
    final DateTime current = now ?? DateTime.now();

    final bool withinTime =
        _lastTapTime != null && current.difference(_lastTapTime!) <= timeout;

    final bool withinDist =
        _lastTapPos != null && (pos - _lastTapPos!).distance <= slop;

    if (withinTime && withinDist) {
      _tapCount = (_tapCount + 1).clamp(1, maxClicks);
    } else {
      _tapCount = 1;
    }

    _lastTapTime = current;
    _lastTapPos = pos;
    return _tapCount;
  }

  /// 重置内部状态。
  ///
  /// 使用场景：
  /// - 手势被取消；
  /// - 组件被销毁；
  /// - 或需要强制断开连击时。
  void reset() {
    _tapCount = 0;
    _lastTapTime = null;
    _lastTapPos = null;
  }

  /// 最近一次计算得到的连击次数（调用 [registerTap] 后更新）。
  int get currentCount => _tapCount;

  /// 最近一次点击的位置（仅用于调试/读取）。
  Offset? get lastPosition => _lastTapPos;

  /// 最近一次点击的时间（仅用于调试/读取）。
  DateTime? get lastTime => _lastTapTime;
}
